package org.simplextensions;

import org.simplextensions.configuration.xml.ExtensionsConfiguration;
import org.simplextensions.configuration.xml.ExtensionsConfiguration.Dependencies;
import org.simplextensions.graph.Graph;
import org.simplextensions.graph.GraphEvent;
import org.simplextensions.graph.GraphEventListenerAdapter;
import org.simplextensions.graph.NodeAlreadyExistsException;
import org.simplextensions.registry.ExtensionRegistry;
import org.simplextensions.registry.IExtensionRegistry;
import org.simplextensions.registry.SimpleXtensionsBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Tomasz Krzyzak, <a
 *         href="mailto:tomasz.krzyzak@gmail.com">tomasz.krzyzak@gmail.com</a>
 * @since 2009-08-12 23:37:37
 * 
 */
public abstract class Simplextensions {

	private static final Logger log = LoggerFactory.getLogger(Simplextensions.class);

	private static final String EXTENSIONS_XML = "extensions.xml";

	/**
	 * creates new instance of serviceRegistry, extensionRegistry and links them
	 * together (serviceregistry as extension of extensionregistry and
	 * extensionregistry as service of serviceregistry)
	 * 
	 * @return
	 * @throws Exception
	 */
	public synchronized static IExtensionRegistry createNewRegistry() {
		return findBundles(new ExtensionRegistry());
	}

	public static ExtensionRegistry findBundles(final ExtensionRegistry extensionRegistry) {
		final Map<ExtensionsConfiguration, URL> urls = new HashMap<ExtensionsConfiguration, URL>();

		final Graph validGraph = new Graph("ValidBundlesGraph");
		validGraph.addGraphListener(new GraphEventListenerAdapter() {
			public void nodeFullyConnected(GraphEvent graphEvent) {
				try {
					extensionRegistry.addBundle(new SimpleXtensionsBundle(extensionRegistry, (ExtensionsConfiguration) graphEvent.data, urls.get(graphEvent.data)));
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		});

		final Graph structureGraph = new Graph("AllBundlesGraph");
		structureGraph.addGraphListener(new GraphEventListenerAdapter() {
			public void nodeFullyConnected(GraphEvent graphEvent) {

				try {
					validGraph.addNode(graphEvent.id, structureGraph.getOutgoingNodesIds(graphEvent.data), graphEvent.data);
				} catch (NodeAlreadyExistsException e) {
					throw new RuntimeException(e);
				}
			}
		});

		log.info("-------------------- finding & registering bundles ---------------");
		try {
			Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(EXTENSIONS_XML);
			log.info("-------------------- end of finding bundles ---------------");

			for (Enumeration<URL> urlList = resources; urlList.hasMoreElements();) {

				URL url = urlList.nextElement();
				ExtensionsConfiguration read = read(url);
				urls.put(read, url);
				log.debug("bundle's configuration file read");
				structureGraph.addNode(read.getName(), convert(read.getDependencies()), read);
				log.debug("bundle registered");

			}
		} catch (JAXBException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		log.info("-------------------- end of finding & registering bundles ---------------");
		return extensionRegistry;
	}

	private static String[] convert(Dependencies dependencies) {
		List<String> dependency = dependencies.getDependency();
		String[] result = new String[dependency.size()];
		dependency.toArray(result);
		return result;
	}

	private static ExtensionsConfiguration read(URL url) throws JAXBException {
		if (log.isTraceEnabled())
			log.trace("reading bundle config from:" + url.toString());
		JAXBContext jc = JAXBContext.newInstance(ExtensionsConfiguration.class.getPackage().getName());
		Unmarshaller u = jc.createUnmarshaller();
		return ((JAXBElement<ExtensionsConfiguration>) u.unmarshal(url)).getValue();
	}

}
